// 用户信息
const router = require('koa-router')()
const jwt = require('jsonwebtoken')
const User = require('../model/userSchema.js')
const Menu = require('../model/menuSchema.js')
const Role = require('../model/roleSchema.js')
const Counter = require('../model/counterSchema.js')
const utils = require('../utils/utils.js')
var svgCaptcha = require('svg-captcha');

//设置路由前缀
router.prefix('/users')

//列表渲染 get 上传 POST 修改PUT或者POST 删除delete或者POST  RESTFUL风格语法
//如果是POST请求 获取前端发送的参数 ctx.request.body
//如果是GET请求 获取前端发送的参数 ctx.request.query
//ctx.body是返回给前端的数据
//方法的后面 第一个参数都是查询条件 第二个参数基本上都是返回条件
//{userPwd:0} 作为返回条件 0代表不返回  1代表返回 {}或者null代表全部返回 'userName userEmail'代表挑选返回
//$inc 自增加  $or或者  $in包含   $all所有



//权限
router.post('/permissionList', async (ctx, next) => {
    let authorization = ctx.request.header.authorization//获取请求头
    let { data } = utils.decoded(authorization)//解密请求头里的token    解密后的data是用户信息
    try {
        //获取菜单权限
        let menuList = await getMenuList(data.role, data.roleList)//第一个参数判断是管理员还是普通用户  第二个参数是角色列表
        //获取按钮权限
        let actionList = getActionList(JSON.parse(JSON.stringify(menuList)))
        ctx.body = utils.success({ data: { menuList, actionList }, msg: 'ok', code: 200 })//返回给前端
    } catch (error) {
        console.log(error);
    }
})
//菜单权限函数
async function getMenuList(userRole, roleKey) {
    let roleList = []
    //如果是管理员
    if (userRole === 0) {
        roleList = await Menu.find({}).lean() || {}//查询所有菜单并返回    如果没有数据返回空对象
    } else {
        //如果是普通用户
        let permissionList = []
        //查询当前登陆用户的角色(可能有多个角色)  $in包含
        let userRoleList = await Role.find({ _id: { $in: roleKey } })//.lean()将查询结果转换为普通的 JavaScript 对象，而不是 mongoose 文档对象
        userRoleList.forEach(item => {
            //checkkeys是当前用户的菜单权限   halfCheckkeys是当前用户的按钮权限
            let { checkkeys, halfCheckkeys } = item.permissionList
            //concat()合并两个或多个数组。这个方法不会改变现有的数组，而是返回一个新的数组
            permissionList = permissionList.concat([...checkkeys, ...halfCheckkeys])
        })
        //new Set()数组去重  类似于数组，但是成员的值都是唯一的，没有重复的值。
        permissionList = [...new Set(permissionList)]
        //查询当前用户的所有菜单权限
        roleList = await Menu.find({ _id: { $in: permissionList } }).lean()//.lean()将查询结果转换为普通的 JavaScript 对象，而不是 mongoose 文档对象
    }
    //转为树形结构数组
    return utils.TreeMenuList(roleList)
}
//按钮权限函数
function getActionList(menuList) {
    let actionList = []
    //递归遍历
    const deep = (arr) => {
        while (arr.length) {//while循环，当arr有值时执行
            let item = arr.pop()//移除并返回数组最后一个元素
            if (item.children && item.action) {//一级菜单
                deep(item.children)//递归继续遍历子节点
            } else if (item.children && !item.action) {//二级菜单
                //如果二级菜单有按钮权限，则添加到actionList数组中
                item.children.map(v => {
                    actionList.push(v.menuCode)
                })
            }
        }
    }
    deep(menuList)//递归遍历
    return actionList
}


//登陆接口
router.post('/login', async (ctx, next) => {
    //解构出前端发送过来的数据
    let { userName, userPwd, captcha } = ctx.request.body
    try {
        //findOne 查询单个数据  第一个参数为查询条件,第二个参数为返回数据
        let res = await User.findOne({ userName, userPwd }, 'userName userId userEmail deptId mobile role roleList state')
        //如果ref存在   判断验证码是否正确toLowerCase()转为小写
        if (res && captcha.toLowerCase() === ctx.session.picCode.toLowerCase()) {//ctx.session后端存储数据
            let data = res._doc
            //jwt.sign生成token     {data:data}为需要加密的数据  'hai'为密钥  {expiresIn:'1h'}为有效时间
            const token = jwt.sign({
                data: data
            }, 'hai', { expiresIn: '1h' })
            data.token = token
            //返回给前端
            ctx.body = utils.success({ data: data, msg: 'ok', code: 200 })
        } else {
            ctx.body = utils.loginErr('账号密码或验证码错误')
        }
    } catch (error) {
        console.log(error);
    }
})


//用户列表查询渲染
router.get('/list', async (ctx) => {
    //解构出前端发送过来的数据
    let { userName, userId, state, type } = ctx.request.query
    //调用分页方法
    let { page, skipIndex } = utils.pager(ctx.request.query)
    try {
        //部门管理获取用户在职名称
        if (type === 'dept') {
            let deptparams = {
                state: 1//为1代表在职
            }
            //查询所有在职员工并返回
            let res = await User.find(deptparams)
            ctx.body = utils.success(res)
        } else {
            let params = {};
            if (userId) params.userId = userId;
            if (userName) params.userName = { $regex: userName };//$regex 正则表达式模糊查询;
            if (state && state != 0) params.state = state;
            // find方法查询所有数据      { _id: 0, userPwd: 0 }为0则不返回给前端
            let query = User.find(params, { _id: 0, userPwd: 0 })
            // skip跳过几条   limit限制返回条数
            let list = await query.skip(skipIndex).limit(page.pagesize)
            // 查询总条数   countDocuments用于计算与选择标准匹配的文档总数量
            let total = await User.countDocuments(params)
            ctx.body = utils.success({ list, total })
        }
    } catch (error) {
        console.log(error);
    }
})
//用户列表新增/编辑
router.post('/operate', async (ctx) => {
    //解构出前端发送过来的数据
    let { userId, userName, userEmail, mobile, job, roleList, state, deptId, active } = ctx.request.body
    try {
        //判断是否是新增还是编辑
        if (active == 'add') {
            //后端表单验证
            if (!userName || !mobile) {
                ctx.body = utils.fail('参数错误')
            }
            //查询传来的数据是否存在    $or操作符允许你在一个查询中使用多个条件，只要满足其中一个条件，文档就会被返回
            let res = await User.findOne({ $or: [{ userName }, { mobile }] }, '_id userName mobile')
            if (res) {
                ctx.body = utils.fail('数据已存在')
            } else {
                //  如果没有数据就新增  自动创建一个表单    第一个参数查找,第二个参数更新,第三个参数返回
                //  findOneAndUpdate查找并更新单个文档    { _id: 'userId'}查找_id   $inc自增加，1代表每次加一   new: true返回新的文档
                let doc = await Counter.findOneAndUpdate({ _id: 'userId' }, { $inc: { squence_value: 1 } }, { new: true })
                let userRes = new User({
                    userId: doc.squence_value,
                    userName,
                    userPwd: '123456',//密码默认123456
                    userEmail,
                    mobile,
                    state: 1,
                    role: 1,
                    roleList,
                    job,
                    deptId
                })
                //存储到数据库
                await userRes.save()
                ctx.body = utils.success('新增成功')
            }
        } else {
            //编辑  findOneAndUpdate()查找并更新单个文档  第一个参数查找,第二个参数更新,第三个参数返回
            let res = await User.findOneAndUpdate({ userId }, { userName, userEmail, mobile, job, roleList, state, deptId })
            ctx.body = utils.success('更新成功')
        }
    } catch (error) {
        console.log(error);
    }
})
//用户列表删除/批量删除
router.post('/delete', async (ctx) => {
    //解构出前端发送过来的数据
    let { userDelList } = ctx.request.body
    // userDelList: [ { userId: 1000015, state: 1 }, { userId: 1000003, state: 1 } ]
    // 在职人员数组
    let noDimissionList = userDelList.filter(item => item.state == 1)
    // 在职人员Id数组
    let userIds1 = noDimissionList.map(item => item.userId)
    // 离职/试用期人员数组
    let dimissionList = userDelList.filter(item => item.state == 2 || item.state == 3)
    // 离职/试用期人员Id数组
    let userIds2 = dimissionList.map(item => item.userId)
    try {
        let sum = 0
        if (userIds1.length > 0) {
            // 转为离职     updateMany更新集合中符合指定条件的多个文档。它接受两个参数：一个是查询条件，另一个是要应用的更新操作
            let { modifiedCount } = await User.updateMany({ userId: { $in: userIds1 } }, { state: 2 })
            sum += modifiedCount//  modifiedCount:修改了多少条数据
        }
        if (userIds2.length > 0) {
            //删除
            let { deletedCount } = await User.deleteMany({ userId: { $in: userIds2 } })
            sum += deletedCount//  deletedCount:删除了多少条数据
        }
        ctx.body = utils.success(`删除成功，删除了${sum}条数据`)
    } catch (error) {
        console.log(error);
    }
})

//验证码接口
router.get('/captcha', async (ctx) => {
    var cap = svgCaptcha.create({
        size: 4, // 验证码长度
        width: 120, //验证码的宽度
        height: 40, //验证码的高度
        fontSize: 45, //验证码的字体大小
        ignoreChars: "0oO1ilI", // 验证码字符中排除 0o1i
        noise: 2, // 干扰线条的数量
        color: true, // 验证码的字符是否有颜色，默认没有，如果设定了背景，则默认有
        background: "#ddd", // 验证码图片背景颜色
    });
    let img = new Buffer.from(cap.data).toString("base64");
    let base64Img = "data:image/svg+xml;base64," + img;
    let text = cap.text.toLowerCase()
    ctx.session.picCode = text
    ctx.body = utils.success({ code: 200, data: { imgurl: `${base64Img}` }, msg: 'ok' })
})








module.exports = router
